package net.lucode.hackware.magicindicator;

import ohos.utils.PlainArray;
import ohos.utils.PlainBooleanArray;

/**
 * 方便扩展IPagerNavigator的帮助类，将ViewPager的3个回调方法转换成 onSelected、onDeselected、onEnter等回调，方便扩展
 *
 * @since 2020-09-29
 */
public class NavigatorHelper {
    private PlainBooleanArray mDeselectedItems = new PlainBooleanArray();
    private PlainArray<Float> mLeavedPercents = new PlainArray<Float>();

    private int mTotalCount;
    private int mCurrentIndex;
    private int mLastIndex;
    private float mLastPositionOffsetSum;
    private int mScrollState;

    private boolean mIsSkimOver;
    private OnNavigatorScrollListener mNavigatorScrollListener;

    /**
     * onPageScrolled
     *
     * @param position position
     * @param positionOffset positionOffset
     * @param positionOffsetPixels positionOffsetPixels
     */
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        float currentPositionOffsetSum = position + positionOffset;
        boolean isLeftToRight = positionOffsetPixels > 0;

        if (mScrollState != ScrollState.SCROLL_STATE_IDLE) {
            if (Math.abs(currentPositionOffsetSum - mLastPositionOffsetSum) < 0.0000001) {
                return;
            }
            int nextPosition = position + 1;
            if (!isLeftToRight) {
                nextPosition = position - 1;
            }

            boolean isNormalDispatch = true;
            if (positionOffset == 0.0f) {
                if (isLeftToRight) {
                    nextPosition = position - 1;
                    isNormalDispatch = false;
                }
            }
            for (int i = 0; i < mTotalCount; i++) {
                if (i == position || i == nextPosition) {
                    continue;
                }
                Float leavedPercent = mLeavedPercents.get(i, 0.0f);
                if (leavedPercent != 1.0f) {
                    dispatchOnLeave(i, 1.0f, isLeftToRight, true);
                }
            }
            if (isNormalDispatch) {
                if (isLeftToRight) {
                    dispatchOnLeave(position, positionOffset, true, false);
                    dispatchOnEnter(nextPosition, positionOffset, true, false);
                } else {
                    dispatchOnLeave(position, positionOffset, false, false);
                    dispatchOnEnter(nextPosition, positionOffset, false, false);
                }
            } else {
                dispatchOnLeave(position, 1.0f - positionOffset, true, false);
                dispatchOnEnter(nextPosition, 1.0f - positionOffset, true, false);
            }
        } else {
            for (int i = 0; i < mTotalCount; i++) {
                if (i == mCurrentIndex) {
                    continue;
                }
                boolean isDeselected = mDeselectedItems.get(i, false);
                if (!isDeselected) {
                    dispatchOnDeselected(i);
                }
                Float leavedPercent = mLeavedPercents.get(i, 0.0f);
                if (leavedPercent != 1.0f) {
                    dispatchOnLeave(i, 1.0f, false, true);
                }
            }
            dispatchOnEnter(mCurrentIndex, 1.0f, false, true);
            dispatchOnSelected(mCurrentIndex);
        }
        mLastPositionOffsetSum = currentPositionOffsetSum;
    }

    private void dispatchOnEnter(int index, float enterPercent, boolean isLeftToRight, boolean isForce) {
        if (mIsSkimOver || index == mCurrentIndex || mScrollState == ScrollState.SCROLL_STATE_DRAGGING || isForce) {
            if (mNavigatorScrollListener != null) {
                mNavigatorScrollListener.onEnter(index, mTotalCount, enterPercent, isLeftToRight);
            }
            mLeavedPercents.put(index, 1.0f - enterPercent);
        }
    }

    private void dispatchOnLeave(int index, float leavePercent, boolean isLeftToRight, boolean isForce) {
        if (mIsSkimOver || index == mLastIndex || mScrollState == ScrollState.SCROLL_STATE_DRAGGING
                || ((index == mCurrentIndex - 1 || index == mCurrentIndex + 1)
                && mLeavedPercents.get(index, 0.0f) != 1.0f) || isForce) {
            if (mNavigatorScrollListener != null) {
                mNavigatorScrollListener.onLeave(index, mTotalCount, leavePercent, isLeftToRight);
            }
            mLeavedPercents.put(index, leavePercent);
        }
    }

    private void dispatchOnSelected(int index) {
        if (mNavigatorScrollListener != null) {
            mNavigatorScrollListener.onSelected(index, mTotalCount);
        }
        mDeselectedItems.put(index, false);
    }

    private void dispatchOnDeselected(int index) {
        if (mNavigatorScrollListener != null) {
            mNavigatorScrollListener.onDeselected(index, mTotalCount);
        }
        mDeselectedItems.put(index, true);
    }

    /**
     * onPageSelected
     *
     * @param position position
     */
    public void onPageSelected(int position) {
        mLastIndex = mCurrentIndex;
        mCurrentIndex = position;
        dispatchOnSelected(mCurrentIndex);
        for (int i = 0; i < mTotalCount; i++) {
            if (i == mCurrentIndex) {
                continue;
            }
            boolean isDeselected = mDeselectedItems.get(i, false);
            if (!isDeselected) {
                dispatchOnDeselected(i);
            }
        }
    }

    /**
     * onPageScrollStateChanged
     *
     * @param state state
     */
    public void onPageScrollStateChanged(int state) {
        mScrollState = state;
    }

    /**
     * setNavigatorScrollListener
     *
     * @param navigatorScrollListener navigatorScrollListener
     */
    public void setNavigatorScrollListener(NavigatorHelper.OnNavigatorScrollListener navigatorScrollListener) {
        mNavigatorScrollListener = navigatorScrollListener;
    }

    /**
     * setSkimOver
     *
     * @param isSkimOver isSkimOver
     */
    public void setSkimOver(boolean isSkimOver) {
        mIsSkimOver = isSkimOver;
    }

    /**
     * getTotalCount
     *
     * @return mTotalCount
     */
    public int getTotalCount() {
        return mTotalCount;
    }

    /**
     * setTotalCount
     *
     * @param totalCount totalCount
     */
    public void setTotalCount(int totalCount) {
        mTotalCount = totalCount;
        mDeselectedItems.clear();
        mLeavedPercents.clear();
    }

    /**
     * getCurrentIndex
     *
     * @return mCurrentIndex
     */
    public int getCurrentIndex() {
        return mCurrentIndex;
    }

    /**
     * getScrollState
     *
     * @return mScrollState
     */
    public int getScrollState() {
        return mScrollState;
    }

    /**
     * OnNavigatorScrollListener interface
     *
     * @since 2020-09-29
     */
    public interface OnNavigatorScrollListener {
        /**
         * onEnter
         *
         * @param index index
         * @param totalCount totalCount
         * @param enterPercent enterPercent
         * @param isLeftToRight isLeftToRight
         */
        void onEnter(int index, int totalCount, float enterPercent, boolean isLeftToRight);

        /**
         * onLeave
         *
         * @param index index
         * @param totalCount totalCount
         * @param leavePercent leavePercent
         * @param isLeftToRight isLeftToRight
         */
        void onLeave(int index, int totalCount, float leavePercent, boolean isLeftToRight);

        /**
         * onSelected
         *
         * @param index index
         * @param totalCount totalCount
         */
        void onSelected(int index, int totalCount);

        /**
         * onDeselected
         *
         * @param index index
         * @param totalCount totalCount
         */
        void onDeselected(int index, int totalCount);
    }
}
